<?php

namespace App\Http\Controllers\Api;

use App\Exceptions\CustomException;
use App\Http\Controllers\Controller;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

/**
|--------------------------------------------------------------------------
| 后台和小程序api交互
|--------------------------------------------------------------------------
 */
class MiniProgramController extends Controller
{
    public static $APPID;
    public static $APP_SECRET;
    public static $MCH_ID;
    public static $NOTIFY_URL;
    public static $STORE;
    public static $KEY;
    public final function __construct()
    {
        self::$APPID = config('wechat.mini_program.default.app_id');
        self::$APP_SECRET = config('wechat.mini_program.default.secret');
        self::$MCH_ID = config('wechat.mini_program.default.mch_id');
        self::$NOTIFY_URL = config('wechat.mini_program.default.notify_url');
        //支付的商家信息
        self::$STORE = config('wechat.mini_program.default.store');
        //支付秘钥
        self::$KEY = config('wechat.mini_program.default.key');
    }

    //获取微信登录授权
    function code2session ($code): array {
        //微信登陆url，带上各种参数
        $wx_auth_url = 'https://api.weixin.qq.com/sns/jscode2session?appid='.self::$APPID.'&secret='.self::$APP_SECRET.'&js_code='.$code.'&grant_type=authorization_code';
        $client = new Client();
        //默认授权失败
        $message = '授权失败';

        $openid = '';
        $unionid = '';
        $session_key = '';
        try {
            $res = $client->get($wx_auth_url);
            $contents = json_decode($res->getBody()->getContents(),1);

            Log::info('contents:');
            Log::info($contents);
            Log::info('wx_info:'.json_encode($contents));

            //错误码
            $errcode = $contents['errcode'] ?? '';
            //错误信息
            $errmsg = $contents['errmsg'] ?? '';
            $errors = [$errmsg];
            switch($errcode) {//授权成功
                case 0:
                    //微信用户的唯一标识
                    $openid = $contents['openid'];
                    //会话密钥
                    $session_key = $contents['session_key'];
                    //用户在微信开放平台的唯一标识符。本字段在满足一定条件的情况下才返回。
                    $unionid = $contents['unionid'] ?? '';
                    $message = '授权成功';
                    break;
                default:
                    break;
            }
        }catch (RequestException $e) {
            Log::info('发起微信登录授权url请求失败');
            Log::info($e->getResponse());

            $errcode = 5;
            $errors = ['url请求失败'];
        }

        return [
            'status_code'   =>  $errcode,
            'message'       =>  $message,
            'errors'        =>  $errors,
            'openid'        =>  $openid,
            'unionid'       =>  $unionid,
            'session_key'   =>  $session_key
        ];
    }

    //获取小程序api凭证并保存到redis
    function getAccessToken() {
        if ($access_token = Redis::get('access_token')) {
            return $access_token;
        }
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.self::$APPID.'&secret=' . self::$APP_SECRET;
        $client = new Client();
        $content = json_decode($client->get($url)->getBody()->getContents(),1);
        Log::info($content);
        //获取成功
        if (isset($content['access_token'])) {
            //redis中过期时间比微信端过期时间短点，保证永远不会取到过期access_token
            Redis::setex('access_token',$content['expires_in'] - 100,$content['access_token']);
            return $content['access_token'];
        }else {
            Log::info('access_token获取失败');
            Log::info($content);
            return 0;
        }
    }


    /**
     * 生成带参数小程序码，可能包括用户参数、商品参数
     * @param $key   string     参数名
     * @param $value string     参数值
     * @param string $page 扫描小程序码后跳转页面
     * @param int $width 小程序码宽度，不传默认430px
     * @return string
     * @throws GuzzleException
     */
    function getUnlimited($key, $value, $page, $width = 430) {
        if ($access_token = $this->getAccessToken()) {
            $url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=' . $access_token;
            $post_data = [
                'scene' =>  $key . '=' . $value,
                'width' =>  $width
            ];

            $client = new Client();
            $res = $client->request('POST',$url,['json' => $post_data]);
            //如果调用成功，会直接返回图片二进制内容，如果请求失败，会返回 JSON 格式的数据。todo 判断返回格式
            $mpcode_file = '/mpcode/' . md5($key . $value . time() . rand(1,10000)) . '.jpg';
            file_put_contents(public_path() . $mpcode_file,$res->getBody()->getContents());
            return config('app.url') . $mpcode_file;
        }else {
            return false;
        }
    }

    /**
     * 统一下单。商户在小程序中先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易后调起支付。
     * @param $body         商品描述
     * @param $order_no     订单号
     * @param $money        订单金额，以分为单位
     * @param $client_ip    终端ip
     * @param $openid
     * @return mixed
     * @throws GuzzleException
     * @throws Exception
     * @throws \Exception
     */
    function unifiedorder($body,$order_no,$money,$client_ip, $openid){
        $client = new Client();
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $log_data = $post_data = array(
            'appid'             =>  self::$APPID,
            'mch_id'            =>  self::$MCH_ID,
            'nonce_str'         =>  random_str(),
            'body'              =>  self::$STORE . '-' . $body,//店名-商品
            'out_trade_no'      => $order_no,
            'total_fee'         =>  $money,//订单总金额，单位为分，
            'spbill_create_ip'  =>  $client_ip,//终端ip
            'notify_url'        =>  self::$NOTIFY_URL,//异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
            'trade_type'        =>  'JSAPI',
            'openid'            =>  $openid
        );
        //生成签名
        $post_data['sign'] = $this->sign($post_data);

        $xml = arr2xml($post_data);
        $content = xml2arr($client->request('post', $url,['headers'=>["Content-type: text/html; charset=utf-8"],'body' => $xml])->getBody()->getContents());

        if ($content['return_code'] == 'SUCCESS') {
            if ($content['result_code'] == 'SUCCESS') {
                //组装数据，再次签名，返回给小程序
                $data = [
                    'appId'     =>  self::$APPID,
                    'timeStamp' =>  strval(time()),
                    'nonceStr'  =>  random_str(),
                    'package'   =>  'prepay_id=' . $content['prepay_id'],
                    'signType'  =>  'MD5'
                ];
                $data['paySign'] = $this->sign($data);
                $data['prepay_id'] = $content['prepay_id'];
                return $data;
            }elseif ($content['result_code'] == 'FAIL') {
                if ($content['err_code'] == 'ORDERPAID') {
                    throw new CustomException("该订单已支付");
                }
                Log::info("支付结果：");
                Log::info(print_r($content, 1));
                throw new \Exception("交易失败");
            }
        }else {
            Log::info('调用统一支付接口失败');
            Log::info('log_data:');
            Log::info(print_r($log_data, 1));
            Log::info('post_data:');
            Log::info(print_r($post_data, 1));
            Log::info('xml：');
            Log::info(print_r($xml,1));
            Log::info("支付结果：");
            Log::info(print_r($content, 1));
            return false;
        }
    }

    //md5对数组生成签名算法
    function sign($data) {
        $arr = [];
        ksort($data);
        foreach ($data as $k=>$v) {
            $arr[] = $k . '=' . $v;
        }
        $str = implode('&',$arr);
        $str .= '&key=' . self::$KEY;
        return strtoupper(md5($str));
    }

    /**企业付款到用户零钱 需要https证书
     * @param $partner_trade_no 商户订单号
     * @param $openid
     * @param $re_user_name 收款人姓名
     * @param $money    提现金额，单位分
     * @return array
     */
    function withdraw($partner_trade_no,$openid,$re_user_name,$money) {
        $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
        $client = new Client();
        $log_data = $post_data = [
            'mch_appid'         =>  self::$APPID,
            'mchid'             =>  self::$MCH_ID,
            'nonce_str'         =>  random_str(),
            'partner_trade_no'  =>  $partner_trade_no,
            'openid'            =>  $openid,
            'check_name'        =>  'FORCE_CHECK',
            're_user_name'      =>  $re_user_name,
            'amount'            =>  $money,//订单总金额，单位为分，
            'desc'              =>  '用户提现'
        ];
        //生成签名
        $post_data['sign'] = $this->sign($post_data);
        //组装发送的xml数据
        $xml = arr2xml($post_data);
        //$content = xml2arr($client->request('POST',$url,['body'=>$xml])->getBody()->getContents());
        $content = [
            'return_code'   =>  'SUCCESS',
            'result_code'   =>  'SUCCESS',
            'payment_no'    =>  'sdfghjk',
            'payment_time'  =>  date('Y-m-d H:i:s')
        ];

        if ($content['return_code'] == 'SUCCESS') { //通讯成功
            if ($content['result_code'] == 'SUCCESS') { //业务结果
                return [
                    'status'        =>  true,
                    //微信付款单号
                    'payment_no'    =>  $content['payment_no'],
                    //付款成功时间
                    'payment_time'  =>  $content['payment_time']
                ];
            }elseif ($content['result_code'] == 'FAIL') {
                Log::info('提现参数：');
                Log::info($log_data);
                Log::info('提现失败：');
                Log::info($content);

                return [
                    'status'        =>  false,
                    'error_code'    =>  $content['err_code'],
                    'err_code_des'  =>  $content['err_code_res']
                ];
            }else {
                return [
                    'status'        =>  false,
                    'error_code'    =>  'UNKNOWN',
                    'err_code_des'  =>  '提现失败'
                ];
            }
        }elseif ($content['return_code'] == 'FAIL') {//通讯失败
            return [
                'status'        =>  false,
                'error_code'    =>  'COMMUNICATE_FAIL',
                'err_code_des'  =>  '通讯失败',
            ];
        }
    }

    /**查询企业付款结果
     * @param $partner_trade_no 商户调用企业付款API时使用的商户订单号
     * @return array
     */
    function get_transfer_info($partner_trade_no) {
        $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo';
        $client = new Client();
        $log_data = $post_data = [
            'nonce_str'         =>  random_str(),
            'partner_trade_no'  =>  $partner_trade_no,
            'mch_id'            =>  self::$MCH_ID,
            'appid'             =>  self::$APPID
        ];
        $post_data['sign'] = $this->sign($post_data);
        $xml = arr2xml($post_data);
        $transfer_result = json_decode($client->request('POST',$url,['body'=>$xml])->getBody()->getContents(),1);

        if ($transfer_result['return_code'] == 'SUCCESS') { //通讯成功
            if ($transfer_result['result_code'] == 'SUCCESS') {
                if ($transfer_result['status'] == 'SUCCESS') {//企业支付成功
                    return [
                        'status'=>true,
                        'detail_id'=> $transfer_result['detail_id'],
                        'payment_time'=>$transfer_result['payment_time']
                    ];
                }elseif ($transfer_result['status'] == 'FAILED') {//失败
                    return ['status'=>false,'err_code'=>'FAILED','err_code_des'=>'支付失败','reason'=>$transfer_result['reason']];
                }elseif ($transfer_result['status'] == 'PROCESSING') {//处理中
                    return ['status'=>false,'err_code'=>'PROCESSING','err_code_des'=>'处理中'];
                }
            }else {
                return [
                    'status'        => false,
                    'err_code'      => $transfer_result['err_code'],
                    'err_code_des'  => $transfer_result['err_code_des']
                ];
            }
        }else {
            return ['status' => false,'err_code' => 'COMMUNICATE_FAIL','err_code_des' => '通讯失败'];
        }
    }

    /**
     *该接口提供所有微信支付订单的查询，商户可以通过查询订单接口主动查询订单状态，完成下一步的业务逻辑。
     * 需要调用查询接口的情况：
     *
     * ◆ 当商户后台、网络、服务器等出现异常，商户系统最终未接收到支付通知；
     * ◆ 调用支付接口后，返回系统错误或未知交易状态情况；
     * ◆ 调用刷卡支付API，返回USERPAYING的状态；
     * ◆ 调用关单或撤销接口API之前，需确认支付状态；
     * 不需要证书
     * @param $no   Sting   商户订单号号
     * @return array
     */
    function payment_query($no) {
        $url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        $client = new Client();
        $log_data = $post_data = [
            'nonce_str'         =>  random_str(),
            //商户订单号
            'out_trade_no'      =>  $no,
            'mch_id'            =>  self::$MCH_ID,
            'appid'             =>  self::$APPID,
            'sign_type'         =>  'MD5'
        ];
        $post_data['sign'] = $this->sign($post_data);
        $xml = arr2xml($post_data);
        $order_result = json_decode($client->request('POST',$url,['body'=>$xml])->getBody()->getContents(),1);

        if ($order_result['return_code'] == 'SUCCESS') { //通讯成功
            if ($order_result['result_code'] == 'SUCCESS') {//业务结果
                if ($order_result['trade_state'] == 'SUCCESS') {//支付状态
                    return [
                        'status'    => true,
                        'time_end'  => $order_result['time_end'],
                        'return_msg'    => $order_result['return_msg']
                    ];
                }elseif ($order_result['trade_state'] == 'USERPAYING') {//支付中
                    return [
                        'status'        => 'USERPAYING',
                        'return_msg'    => $order_result['return_msg']
                    ];
                }else {
                    return [
                        'status'        => false,
                        'err_code'      => $order_result['err_code'],
                        'err_code_des'  => $order_result['err_code_des'],
                        'return_msg'    => $order_result['return_msg']
                    ];
                }
            }else {
                return [
                    'status'        => false,
                    'err_code'      => $order_result['err_code'],
                    'err_code_des'  => $order_result['err_code_des'],
                    'return_msg'    => $order_result['return_msg']
                ];
            }
        }else {
            return ['status' => false,'err_code' => 'COMMUNICATE_FAIL','err_code_des' => '通讯失败'];
        }
    }
}
